/**
 * @author Yunfeng Bai
 *
 * 2012-9-2
 */
package shader;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import javax.media.opengl.GL2;

public class ShaderProgram
{
	int vertexShaderId, fragmentShaderId;
	String vertexShaderCode, fragmentShaderCode;
	int programId;
	
	public ShaderProgram(GL2 gl, String vertexShaderFile, String fragmentShaderFile)
	{
		// Create shaders.
		int vertexShaderId = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
		int fragmentShaderId = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
		
		// Load shader code.
		vertexShaderCode = readFile(vertexShaderFile);
		fragmentShaderCode = readFile(fragmentShaderFile);
		
		// Fill in shader code.
		gl.glShaderSource(vertexShaderId, 1, new String[] { vertexShaderCode }, null);
		gl.glShaderSource(fragmentShaderId, 1, new String[] { fragmentShaderCode }, null);
		
		// Compile shaders.
		gl.glCompileShader(vertexShaderId);
		gl.glCompileShader(fragmentShaderId);
		
		// Check shader status.
		byte[] infoBytes = null;
		ByteBuffer info = null;
		IntBuffer infoLength = IntBuffer.allocate(1);
		gl.glGetShaderiv(vertexShaderId, GL2.GL_INFO_LOG_LENGTH, infoLength);
		if (infoLength.get(0) > 1)
		{
			info = ByteBuffer.allocate(infoLength.get(0));
			gl.glGetShaderInfoLog(vertexShaderId, infoLength.get(0), null, info);
			infoBytes = new byte[infoLength.get(0)];
			info.get(infoBytes);
			System.out.println("Vertex shader compiling errors:");
			System.out.println(new String(infoBytes));
		}
		
		gl.glGetShaderiv(fragmentShaderId, GL2.GL_INFO_LOG_LENGTH, infoLength);
		if (infoLength.get(0) > 1)
		{
			info = ByteBuffer.allocate(infoLength.get(0));
			gl.glGetShaderInfoLog(fragmentShaderId, infoLength.get(0), null, info);
			infoBytes = new byte[infoLength.get(0)];
			info.get(infoBytes);
			System.out.println("fragment shader compiling errors:");
			System.out.println(new String(infoBytes));
		}
		
		// Create the program and attach the shaders.
		programId = gl.glCreateProgram();
		gl.glAttachShader(programId, vertexShaderId);
		gl.glAttachShader(programId, fragmentShaderId);
		
		// Link the program.
		gl.glLinkProgram(programId);
		
		// Check the program.
		gl.glGetShaderiv(programId, GL2.GL_INFO_LOG_LENGTH, infoLength);
		if (infoLength.get(0) > 1)
		{
			info = ByteBuffer.allocate(infoLength.get(0));
			gl.glGetShaderInfoLog(programId, infoLength.get(0), null, info);
			infoBytes = new byte[infoLength.get(0)];
			info.get(infoBytes);
			System.out.println("shader linking errors:");
			System.out.println(new String(infoBytes));
		}
	}
	
	public int getUniformLocation(GL2 gl, String str)
	{
		return gl.glGetUniformLocation(programId, str);
	}
	
	public void useProgram(GL2 gl)
	{
		gl.glUseProgram(programId);
	}
	
	public void unuseProgram(GL2 gl)
	{
		gl.glUseProgram(0);
	}
	
	private String readFile(String filename)
	{
		StringBuilder file = new StringBuilder();
		try
		{
			BufferedReader reader = new BufferedReader(new FileReader(filename));
			String line;
			while ((line = reader.readLine()) != null)
			{
				file.append(line + "\n");
			}
			reader.close();
		} catch (IOException e)
		{
			e.printStackTrace();
		}
		
		return file.toString();
	}
}
